import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;

public class DNSClient {
	private static final int SERVER_PORT = 25252;
	private static final String SERVER_ADDRESS = "127.0.0.1";
	private static final int TIMEOUT = 5000;
	private static final int MAXATTEMPTS = 5;
	private DatagramSocket socket;
	private DNSClientFrame frame;

	public DNSClient() {
		try {
			socket = new DatagramSocket();
			socket.setSoTimeout(TIMEOUT);

			EventQueue.invokeLater(new Runnable() {
				public void run() {
					try {
						frame = new DNSClientFrame();
						frame.setVisible(true);
						frame.setActionListener(new SendMessageListener());
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});

			// GESTORE DELLA CHIUSURA DELL'APPLICAZIONE
			Runtime.getRuntime().addShutdownHook(new Thread() {
				@Override
				public void run() {
					socket.close();
					System.out.println("Chiusura connessione");
				}
			});

		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Handler per la gestione dell'evento di pressione del pulsante Send
	 * 
	 */
	private class SendMessageListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {

			// Effettuo la richiesta al DNS server su un thread separato
			new Thread(new Runnable() {

				@Override
				public void run() {
					String domain = frame.getDomain();
					if (!domain.equals("")) {
						// Disabilito il pulsante di invio durante il tentativo
						// di connessione
						frame.setSendEnabled(false);
						try {
							Message m = null;
							DatagramPacket receivedPacket = null;
							int i = 0;
							boolean receivedAnswer = false;
							// Invio messaggio di richiesta per un numero
							// massiomo di tentativi
							while (!receivedAnswer && i < MAXATTEMPTS) {
								try {
									frame.setInformation("Tentativo di connessione "
											+ (i + 1) + " di " + MAXATTEMPTS);

									// Invio messaggio
									socket.send(createDatagram(domain));

									// Attendo risposta
									byte[] receivedData = new byte[1024];
									receivedPacket = new DatagramPacket(
											receivedData, receivedData.length);

									socket.receive(receivedPacket);
									receivedAnswer = true;
								} catch (SocketTimeoutException ex) {
									// Nessuna risposta ottenuta
									i++;
								}

							}

							// Se ho ricevuto un messaggio
							if (receivedAnswer) {
								// Ricostruisco il messaggio
								ObjectInputStream iStream = new ObjectInputStream(
										new ByteArrayInputStream(receivedPacket
												.getData()));
								m = (Message) iStream.readObject();
								iStream.close();

								frame.showMessage(m);
							}
							// In caso di errore
							else {
								frame.setError("Impossibile contattare il server");
							}

							// Riabilito il pulsante di invio
							frame.setSendEnabled(true);

						} catch (IOException e1) {
							e1.printStackTrace();
						} catch (ClassNotFoundException e1) {
							e1.printStackTrace();
						}
					} else {
						// Nessun dominio inserito dall'utente
						frame.setInformation("Inserire un dominio");
					}

				}
			}).start();

		}
	}

	/**
	 * Consente di creare un datagramma UDP contenente una stringa come payload
	 * 
	 * @param domain
	 *            Stringa rappresentante il dominio
	 * @return Il datagramma UDP da inviare sulla rete
	 * @throws IOException
	 */
	private DatagramPacket createDatagram(String domain) throws IOException {
		ByteArrayOutputStream bStream = new ByteArrayOutputStream();
		ObjectOutput oo = new ObjectOutputStream(bStream);
		oo.writeObject(domain);
		oo.close();
		byte sendData[] = new byte[1024];
		sendData = bStream.toByteArray();
		bStream.close();

		DatagramPacket sendPacket = new DatagramPacket(sendData,
				sendData.length, InetAddress.getByName(SERVER_ADDRESS),
				SERVER_PORT);
		return sendPacket;
	}

	public static void main(String args[]) {
		new DNSClient();
	}

}
